/*
 * @This File is Part of Sakura by SaeruHikari: 
 * @Description: Copyright SaeruHikari
 * @Version: 0.1.0
 * @Autor: SaeruHikari
 * @Date: 2020-02-09 17:51:06
 * @LastEditors  : SaeruHikari
 * @LastEditTime : 2020-02-09 18:38:22
 */
#include "../T2String.h"
#include <cstring>

namespace Sakura
{
    static void init_itoh_table(uint16* p)
    {
        for(auto i = 0u; i < 256; ++i)
        {
            char* b = (char*)(p+i);
            b[0] = "0123456789abcdef"[i >> 4];
            b[1] = "0123456789abcdef"[i & 0x0f];
        }
    }    

   static void init_itoa_table(uint32* p) 
   {
        for (int i = 0; i < 10000; ++i) 
        {
            char* b = (char*)(p + i);
            b[3] = (char)(i % 10 + '0');
            b[2] = (char)(i % 100 / 10 + '0');
            b[1] = (char)(i % 1000 / 100 + '0');
            b[0] = (char)(i / 1000);
            // digits of i: (b[0] >> 4) + 1 
            if (i > 999) {
                b[0] |= (3 << 4); // 0x30
            } else if (i > 99) {
                b[0] |= (2 << 4); // 0x20
            } else if (i > 9) {
                b[0] |= (1 << 4); // 0x10
            }
        }    
    }

    static sinline uint16* create_itoh_table()
    {
        static uint16 itoh_table[256];
        init_itoh_table(itoh_table);
        return itoh_table;
    }

    static sinline uint32* create_itoa_table()
    {
        static uint32 itoa_table[10000];
        init_itoa_table(itoa_table);
        return itoa_table;
    }

    static sinline uint16* get_itoh_table()
    {
        static uint16* itoh_table = create_itoh_table();
        return itoh_table;
    }

    static sinline uint32* get_itoa_table()
    {
        static uint32* itoa_table = create_itoa_table();
        return itoa_table;
    }

    size_t u32toh(uint32 val, char* buf)
    {
        static uint16* itoh_table = get_itoh_table();
        uint16 b[4], *p = b + 4;
        do{
            *--p = itoh_table[val & 0xff];
            val >>= 8;
        }while(val > 0);
        
        buf[0] = '0';
        buf[1] = 'x';
        int len = (int) ((char*)(b + 4) - (char*)p - (*(char*)p == '0'));
        memcpy(buf + 2, (char*)(b + 4) - len, (size_t)len);
        return len + 2;
    }

    size_t u64toh(uint64 v, char* buf) 
    {
        static uint16* itoh_table = get_itoh_table();
        uint16 b[8], *p = b + 8;

        do {
            *--p = itoh_table[v & 0xff];
            v >>= 8;
        } while (v > 0);

        buf[0] = '0';
        buf[1] = 'x';
        int len = (int) ((char*)(b + 8) - (char*)p - (*(char*)p == '0'));
        memcpy(buf + 2, (char*)(b + 8) - len, (size_t)len);
        return len + 2;
    }

    size_t u32toa(uint32 v, char* buf) 
    {
        static uint32* itoa_table = get_itoa_table();
        uint32 b[3], *p = b + 2;

        if (v > 9999) {
            b[1] = v / 10000;
            b[2] = itoa_table[v - b[1] * 10000] | 0x30303030;
            --p;
        } else {
            b[2] = itoa_table[v];
            goto u32toa_end;
        }

        if (b[1] > 9999) {
            b[0] = b[1] / 10000;
            b[1] = itoa_table[b[1] - b[0] * 10000] | 0x30303030;
            b[0] = itoa_table[b[0]];
            --p;
        } else {
            b[1] = itoa_table[b[1]];
            goto u32toa_end;
        }
u32toa_end:
        int len = (int) (((char*)b) + 9 + ((*(char*)p) >> 4) - ((char*)p));
        memcpy(buf, ((char*)b) + 12 - len, (size_t)len);
        return len;
    }

    size_t u64toa(uint64 v, char* buf) 
    {
        static uint32* itoa_table = get_itoa_table();
        uint32 b[5], *p = b + 4;
        uint64 x;

        if (v > 9999) {
            x = v / 10000;
            b[4] = itoa_table[v - x * 10000] | 0x30303030;
            --p;
        } else {
            b[4] = itoa_table[v];
            goto u64toa_end;
        }

        if (x > 9999) {
            v = x / 10000;
            b[3] = itoa_table[x - v * 10000] | 0x30303030;
            --p;
        } else {
            b[3] = itoa_table[x];
            goto u64toa_end;
        }

        if (v > 9999) {
            x = v / 10000;
            b[2] = itoa_table[v - x * 10000] | 0x30303030;
            --p;
        } else {
            b[2] = itoa_table[v];
            goto u64toa_end;
        }
        
        if (x > 9999) {
            b[0] = (uint32) (x / 10000);
            b[1] = itoa_table[x - b[0] * 10000] | 0x30303030;
            b[0] = itoa_table[b[0]];
            --p;
        } else {
            b[1] = itoa_table[x];
            goto u64toa_end;
        }
    u64toa_end:
        int len = (int) (((char*)b) + 17 + ((*(char*)p) >> 4) - ((char*)p));
        memcpy(buf, ((char*)b) + 20 - len, (size_t)len);
        return len;
    }

#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#endif

    size_t ftoa(float v, char* buf) 
    {
    #ifdef _WIN32
        return _snprintf_s(buf, 24, 24, "%.7g", v);
    #else
        return snprintf(buf, 24, "%.7g", v);
    #endif
    }

    size_t dtoa(double v, char* buf) 
    {
  #ifdef _WIN32
        return _snprintf_s(buf, 24, 24, "%.7g", v);
  #else
        return snprintf(buf, 24, "%.7g", v);
  #endif
    }


#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
#pragma GCC diagnostic pop
#endif
}